package com.znjc.device.modbus.rtu.slave;

import com.intelligt.modbus.jlibmodbus.Modbus;
import com.intelligt.modbus.jlibmodbus.data.ModbusHoldingRegisters;
import com.intelligt.modbus.jlibmodbus.exception.IllegalDataAddressException;
import com.intelligt.modbus.jlibmodbus.exception.IllegalDataValueException;
import com.intelligt.modbus.jlibmodbus.exception.ModbusIOException;
import com.intelligt.modbus.jlibmodbus.serial.SerialParameters;
import com.intelligt.modbus.jlibmodbus.serial.SerialPort;
import com.intelligt.modbus.jlibmodbus.serial.SerialPortException;
import com.intelligt.modbus.jlibmodbus.slave.ModbusSlave;
import com.intelligt.modbus.jlibmodbus.slave.ModbusSlaveFactory;
import com.znjc.common.constant.DeviceConstants;
import com.znjc.common.core.redis.RedisCache;
import com.znjc.common.utils.StringUtils;
import com.znjc.device.modbus.config.MoudbusTypeConfig;
import com.znjc.device.modbus.rtu.slave.config.MoudbusRtuConfig;
import com.znjc.device.modbus.rtu.slave.listener.EventListeners;
import com.znjc.device.modbus.rtu.slave.util.MyDataHolder;
import com.znjc.device.modbus.tcp.slave.ModbusSlaveUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.concurrent.Executors;

@Component
public class JlibModbusSlaveRtuWriter implements CommandLineRunner {
    // 使用日志框架替代System.out
    private static final Logger logger = LoggerFactory.getLogger(ModbusSlaveUtil.class);
    @Resource
    RedisCache redisCache;
    @Resource
    MoudbusRtuConfig moudbusRtuConfig;
    @Resource
    MoudbusTypeConfig moudbustypeConfig;

    private ModbusSlave modbusSlave;

    @Override
    public void run(String... args) throws Exception {
        if(0 == moudbustypeConfig.getType()){
            // 延迟10秒启动，等待其他服务初始化完成
            Thread.sleep(10000);
            logger.info("Starting Modbus RTU slave initialization...");
            // 使用线程池替代直接创建线程
            Executors.newSingleThreadExecutor().submit(this::createSlave);
        }
    }

    /**
     * 创建并启动Modbus从站
     */
    private void createSlave() {
        int retryCount = 3;
        for (int i = 0; i < retryCount; i++) {
            try {
                initSlave();
                createDataHolder();
                logger.info("连接已成功建立。");
                return;
            } catch (Exception e) {
                logger.error("连接尝试 {} 失败: {}", i+1, e.getMessage());
                if (i == retryCount - 1) {
                    logger.error("TCP 所有连接尝试均失败。正在关闭。。。");
                    closeSlave();
                    break;
                }
                try {
                    Thread.sleep(5000); // 5秒后重试
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                }
            }
        }
    }

    /**
     * 初始化Modbus从站配置
     */
    private void initSlave() throws SerialPortException {
        logger.info("Initializing Modbus connection to {}:{}",
                moudbusRtuConfig.getDevice(), moudbusRtuConfig.getSlaveId());
        SerialParameters serialParameters = new SerialParameters();
        serialParameters.setDevice(moudbusRtuConfig.getDevice());
        serialParameters.setBaudRate(SerialPort.BaudRate.getBaudRate(moudbusRtuConfig.getBaudRate()));
        serialParameters.setDataBits(moudbusRtuConfig.getDataBits());
        serialParameters.setStopBits(moudbusRtuConfig.getStopBits());
        serialParameters.setParity(SerialPort.Parity.getParity(moudbusRtuConfig.getParity()));
        modbusSlave = ModbusSlaveFactory.createModbusSlaveRTU(serialParameters);
        modbusSlave.setServerAddress(moudbusRtuConfig.getSlaveId());
        modbusSlave.setReadTimeout(moudbusRtuConfig.getReadTimeout());
        Modbus.setLogLevel(Modbus.LogLevel.LEVEL_DEBUG);
        logger.debug("RTU parameters initialized: {}", serialParameters);
    }

    public void createDataHolder() throws ModbusIOException {
        MyDataHolder myDataHolder = new MyDataHolder();
        myDataHolder.addEventListener(new EventListeners() {
            @Override
            public void readHoldingRegister(int address) {
                System.out.println("使用03功能码读取保持寄存器的数据 address = " + address);
            }

            @Override
            public void readHoldingRegisterRange(int address, int quantity) {
                try {
                    ModbusHoldingRegisters integers = updateHoldingRegisters(address, quantity, RegisterType.HOLDING);
                    modbusSlave.getDataHolder().setHoldingRegisters(integers);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.println("使用03功能码范围读取保持寄存器的数据 address = " + address + " 读取寄存器个数：" + quantity);
            }

            @Override
            public void writeHoldingRegister(int address, int value) throws IllegalDataValueException, IllegalDataAddressException {
                System.out.println("使用03功能码写入保持寄存器 address = " + address + " 值：" + value);
            }

            @Override
            public void writeHoldingRegisterRange(int address, int[] values) {
                System.out.println("使用03功能码批量写入保持寄存器 address = " + address + " 值：" + values.toString());
            }
        });
        modbusSlave.setDataHolder(myDataHolder);
        modbusSlave.listen();
        logger.info("RTU Data holder initialized and listening");
    }

    /**
     * 关闭从站连接
     */
    private void closeSlave() {
        try {
            if (modbusSlave != null && modbusSlave.isListening()) {
                modbusSlave.shutdown();
                logger.info("Modbus slave shutdown successfully");
            }
        } catch (ModbusIOException e) {
            logger.error("Error shutting down Modbus slave", e);
        }
    }

    /**
     * 更新寄存器值（通用处理）
     * @param offset 寄存器起始地址
     * @param quantity 寄存器数量
     * @param type 寄存器类型
     * @return 寄存器数据数组
     */
    private ModbusHoldingRegisters updateHoldingRegisters(int offset, int quantity, RegisterType type)
            throws IllegalDataAddressException, IllegalDataValueException {

        // 每个float占2个寄存器，总寄存器数=quantity*2
        int totalRegisters = quantity * 2;
        ModbusHoldingRegisters hr = new ModbusHoldingRegisters(totalRegisters);

        // 遍历所有请求的寄存器地址
        for (int i = 0; i < quantity; i++) {
            int currentOffset = offset + (i * 2); // float跨两个寄存器
            String redisKey = DeviceConstants.DEVICE_VALUE + currentOffset;
            String value = redisCache.redisTemplate.opsForValue().get(redisKey).toString();
            float floatValue = parseFloatSafely(value);

            // 设置当前偏移量的寄存器值
            hr.setFloat32At(currentOffset, floatValue);
        }

        // 设置到对应的寄存器类型
        if (type == RegisterType.HOLDING) {
            modbusSlave.getDataHolder().setHoldingRegisters(hr);
        } else {
            modbusSlave.getDataHolder().setInputRegisters(hr);
        }
        return hr;
    }


    /**
     * 安全转换字符串到float
     */
    private float parseFloatSafely(String value) {
        try {
            return StringUtils.isEmpty(value) ? 0.0f : Float.parseFloat(value);
        } catch (NumberFormatException e) {
            logger.warn("Invalid float value: {}", value);
            return 0.0f;
        }
    }

    /**
     * 寄存器类型枚举
     */
    private enum RegisterType {
        HOLDING, INPUT
    }

}
